package com.leyou.auth.service;

import com.leyou.auth.config.JwtProperties;
import com.leyou.common.auth.dto.Payload;
import com.leyou.common.auth.dto.UserInfo;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.UserClient;
import com.leyou.user.dto.UserDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * @author 虎哥
 */
@Service
public class AuthService {

    @Autowired
    private UserClient userClient;

    @Autowired
    private JwtProperties prop;

    public void login(String username, String password, HttpServletResponse response) {
        // 	1.根据用户名和密码查询用户（ly-user)
        UserDTO user = null;
        try {
            user = userClient.queryUserByUsernameAndPassword(username, password);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        //	2.判断用户是否为空
        if(user == null){
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        //	3.不为空，获取私钥，生成JWT
        String token = JwtUtils.generateTokenExpireInMinutes(
                new UserInfo(user.getId(), user.getUsername(), "guest"),
                prop.getPrivateKey(),
                prop.getUser().getExpire());
        //	4.写入用户cookie
        writerToken(response, token);
    }

    private void writerToken(HttpServletResponse response, String token) {
        CookieUtils.newCookieBuilder()
                .response(response)
                .httpOnly(true)
                .domain(prop.getUser().getDomain())
                .name(prop.getUser().getCookieName())
                .value(token)
                .build();
    }

    public UserInfo verify(HttpServletRequest request, HttpServletResponse response) {
        // 1.获取cookie中的token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        // 2.利用公钥解析，验证
        Payload<UserInfo> payload = null;
        try {
            // 2.1.token有效性校验
            payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), UserInfo.class);
            // 2.2.校验黑名单
            Boolean exists = redisTemplate.hasKey(payload.getId());
            if(exists!=null&&exists){
                // 黑名单中存在，说明是无效的token
                throw new RuntimeException("token存在与黑名单中，已经失效！");
            }
        } catch (Exception e) {
            // 解析失败，说明未登录，或者登录已经超时。
            throw new LyException(ExceptionEnum.UNAUTHORIZED, e);
        }


        // 3.验证通过，刷新token
        // 3.1.获取token的剩余有效期
        long remainTime = payload.getExpiration().getTime() - System.currentTimeMillis();
        // 3.2.判断是否即将到期
        if(remainTime <= prop.getUser().getRefreshTimeThreshold()){
            // 3.3.生成token
            token = JwtUtils.generateTokenExpireInMinutes(
                    payload.getUserInfo(),
                    prop.getPrivateKey(),
                    prop.getUser().getExpire());
            // 3.4.覆盖cookie
            writerToken(response, token);
        }
        // 4.验证通过，获取用户信息，返回
        return payload.getUserInfo();
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    public void logout(HttpServletRequest request, HttpServletResponse response) {
        // 	1.获取cookie中的token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        //	2.记录黑名单
        //		2.1.解析token，获取id
        Payload<Object> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey());
        String id = payload.getId();
        //		2.2.获取token剩余有效期
        long remainTime = payload.getExpiration().getTime() - System.currentTimeMillis();
        //		2.3.把token的id存入redis，有效期等于token的剩余有效期
        redisTemplate.opsForValue().set(id, "1", remainTime, TimeUnit.MILLISECONDS);
        //	3.服务端删除cookie
        CookieUtils.deleteCookie(
                prop.getUser().getCookieName(), prop.getUser().getDomain(), response);
    }
}
